/**
  ******************************************************************************
  * @file    tx_user.c
  * @author  Iron
  * @date    2021-01-01
  * @version v1.0
  * @brief   tx_user c file
  */

/** @addtogroup GROUP_TEMPLATE
  * @{
  */

/* Private includes ----------------------------------------------------------*/
#include "tx_api.h"
#include "bsp.h"
#include "bsp_led.h"
#include "shell-serial.h"

/* BEBUG LOG */
#include "debug_log.h"
#define LOG_LOCAL_LEVEL     DBG_LOG_DEBUG    // DBG_LOG_DEBUG DBG_LOG_INFO
DBG_LOG_TAG("MAIN_TASK");

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

/* tx os heap */
static TX_BYTE_POOL os_heap;

/* tx timer thread info */
#ifndef TX_NO_TIMER
    extern TX_THREAD _tx_timer_thread;
#endif

/* main task */
#define TX_USER_MAIN_STACK_SIZE         1024 // TX_MINIMUM_STACK
static TX_THREAD thread_main;

#if DEBUG_LOG_ENABLE
    const char *threadx_logo_str =
    _LINE_END_
    "  _____ _                    _     "       _LINE_END_
    " |_   _| |_  _ _ ___ __ _ __| |_ __"       _LINE_END_
    "   | | | ' \\| '_/ -_) _` / _` \\ \\ /"    _LINE_END_
    "   |_| |_||_|_| \\___\\__,_\\__,_/_\\_\\"  _LINE_END_
    _LINE_END_;
#endif

/* Private function prototypes -----------------------------------------------*/

/*----------------------------------------------------------------------------*/
void app_start_logo(void)
{
    DBG_PUTS(threadx_logo_str);

    DBG_LOGD(TAG, "Application start.");
    DBG_LOGD(TAG, "MCU System Core Clock: %u Hz", SystemCoreClock);
    DBG_LOGD(TAG, "MCU HAL Tick Freq    : %u Hz", 1000 / HAL_GetTickFreq());
    DBG_LOGD(TAG, "Threadx OS Tick Freq : %u Hz",  TX_TIMER_TICKS_PER_SECOND);
    DBG_LOGD(TAG, "Threadx OS Version   : %d.%d.%d", THREADX_MAJOR_VERSION, THREADX_MINOR_VERSION, THREADX_PATCH_VERSION);
}

/*----------------------------------------------------------------------------*/
VOID tx_user_stack_error_handler(TX_THREAD *thread_ptr)
{
    DBG_LOGE(TAG, "'%s' thread stack overflow", thread_ptr->tx_thread_name);
}


/*------------------------------------------------------------------------------
 shell commands
------------------------------------------------------------------------------*/
static void cmd_threads_log_info(shell_context_t *shell, TX_THREAD *thread)
{
    double cpu_time = tx_time_get();
    double cpu_usage = thread->tx_thread_run_count;

    cpu_usage = cpu_usage / cpu_time;

    shell->shell_printf(
        "%-20s %-10u %.1f%%/%-8u %d/%d/%u\r\n",
        thread->tx_thread_name,
        thread->tx_thread_priority,
        cpu_usage, // CPU 使用率
        thread->tx_thread_run_count, // CPU 计数器
        (INT)thread->tx_thread_stack_highest_ptr - (INT)thread->tx_thread_stack_start, // 任务栈最小剩余
        (INT)thread->tx_thread_stack_ptr - (INT)thread->tx_thread_stack_start, // 任务栈当前剩余
        (ULONG)thread->tx_thread_stack_size // 任务栈大小
    );
}

static char cmd_threads(shell_context_t *shell, char *args)
{
    extern TX_THREAD *_tx_thread_created_ptr;
    TX_THREAD *next_thread = _tx_thread_created_ptr;
    UINT cpu_time = tx_time_get();

    shell->shell_printf("CPU Time: %u\r\n", cpu_time);
    shell->shell_printf("%-20s %-10s %-13s %s\r\n", "THREAD_NAME", "PRIORITY", "CPU_USAGE", "STATCK(mini/free/size)");

    do
    {
        cmd_threads_log_info(shell, next_thread);
        next_thread = next_thread->tx_thread_created_next;
    }
    while (next_thread && next_thread != _tx_thread_created_ptr);

    return NULL;
}

/*----------------------------------------------------------------------------*/
static char cmd_heap(shell_context_t *shell, char *args)
{
    CHAR *name = NULL;
    ULONG available_bytes = 0;
    ULONG fragments = 0;
    extern VOID *_tx_initialize_unused_memory;
    ULONG heap_size = ((UINT)_TX_LAST_UNUSED_MEMORY - (UINT)_tx_initialize_unused_memory);

    tx_byte_pool_info_get(&os_heap, &name, &available_bytes, &fragments, NULL, NULL, NULL);

    shell->shell_printf(
        "os_heap info, name '%s', addr: 0x%X, size: %u, free: %lu, fragments: %lu\r\n",
        name,
        (UINT)_tx_initialize_unused_memory,
        heap_size,
        available_bytes,
        fragments
    );

    return NULL;
}

/*----------------------------------------------------------------------------*/
static char cmd_led_blink(shell_context_t *shell, char *args)
{
    ULONG ticks;

    ticks = tx_time_get();

    shell->shell_printf("led on, %u\r\n", tx_time_get());

    bsp_led_on(0);
    tx_thread_sleep(TX_MS_TO_TICKS(1000) - (tx_time_get() - ticks));

    ticks = tx_time_get();

    shell->shell_printf("led off, %u\r\n", tx_time_get());

    bsp_led_off(0);
    tx_thread_sleep(TX_MS_TO_TICKS(1000) - (tx_time_get() - ticks));

    return NULL;
}

/*---------------------------------------------------------------------------*/
const struct shell_command_t tx_user_shell_commands[] =
{
    { "led_blink",       cmd_led_blink,                 "'> led_blink': led blink test" },
    { "heap",            cmd_heap,                      "'> heap': log heap info"       },
    { "threads",         cmd_threads,                   "'> threads': log threads info" },
    { NULL, NULL, NULL },
};

static struct shell_command_set_t tx_user_shell_command_set =
{
    .next = NULL,
    .commands = tx_user_shell_commands,
};

/*----------------------------------------------------------------------------*/
/* Define the main threads.  */
void thread_main_entry(ULONG thread_input)
{
    /* serial shell loop forerver */
    shell_serial(&tx_user_shell_command_set);
}

/*----------------------------------------------------------------------------*/
void tx_application_define(void *first_unused_memory)
{
    CHAR *pointer = TX_NULL;

    /* Board Init */
    bsp_init();

    /* led heartbeat blink */
    bsp_led_bink(0, BSP_LED_OFF, 5, 10, BSP_LED_BLINK_FOREVER); // PC13
    bsp_led_bink(1, BSP_LED_OFF, 5, 10, BSP_LED_BLINK_FOREVER); // PB9

    /* app start logo */
    app_start_logo();

    /* Create a byte memory pool from which to allocate the thread stacks.  */
    tx_byte_pool_create(&os_heap, "heap", first_unused_memory, ((UINT)_TX_LAST_UNUSED_MEMORY - (UINT)first_unused_memory));

    /* Allocate the stack for thread 0.  */
    tx_byte_allocate(&os_heap, (VOID **) &pointer, TX_USER_MAIN_STACK_SIZE, TX_NO_WAIT);

    /* Create the main thread.  */
    tx_thread_create(&thread_main, "thread-main", thread_main_entry, 0, pointer,
                     TX_USER_MAIN_STACK_SIZE, TX_USER_THREAD_PRIORITY_NORMAL, TX_USER_THREAD_PRIORITY_NORMAL, TX_NO_TIME_SLICE,
                     TX_AUTO_START);

    /* Registers an application stack error handler. */
    tx_thread_stack_error_notify(tx_user_stack_error_handler);
}

/*----------------------------------------------------------------------------*/
void *tx_user_malloc(size_t size)
{
    void *pointer = NULL;
    UINT status;

    status = tx_byte_allocate(&os_heap, (VOID **) &pointer, size, TX_NO_WAIT);

    if (status != TX_SUCCESS)
        return NULL;

    return pointer;
}

/*----------------------------------------------------------------------------*/
void tx_user_free(void *ptr)
{
    UINT status;

    status = tx_byte_release(ptr);

    if (status != TX_SUCCESS)
    {
        Error_Handler();
    }
}


/**
  * @}
  */

/******************* (C)COPYRIGHT 2021 ***** END OF FILE *********************/
